package com.museum.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.museum.entity.Exhibition;
import com.museum.entity.Reservation;
import com.museum.entity.ReservationVisitor;
import com.museum.entity.SystemSettings;
import com.museum.entity.TicketDaily;
import com.museum.entity.Visitor;
import com.museum.mapper.ExhibitionMapper;
import com.museum.mapper.ReservationMapper;
import com.museum.mapper.ReservationVisitorMapper;
import com.museum.mapper.TicketDailyMapper;
import com.museum.mapper.VisitorMapper;
import com.museum.service.ReservationService;
import com.museum.service.SystemSettingsService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.core.PreparedStatementSetter;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.jdbc.core.simple.SimpleJdbcInsert;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.sql.PreparedStatement;
import java.sql.Statement;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class ReservationServiceImpl extends ServiceImpl<ReservationMapper, Reservation> implements ReservationService {
    
    private static final Logger log = LoggerFactory.getLogger(ReservationServiceImpl.class);

    @Autowired
    private ReservationMapper reservationMapper;

    @Autowired
    private ExhibitionMapper exhibitionMapper;
    
    @Autowired
    private TicketDailyMapper ticketDailyMapper;
    
    @Autowired
    private ObjectMapper objectMapper;
    
    @Autowired
    private SystemSettingsService systemSettingsService;

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Autowired
    private ReservationVisitorMapper reservationVisitorMapper;
    
    @Autowired
    private VisitorMapper visitorMapper;

    @Override
    public Page<Reservation> pageList(Page<Reservation> page, Integer status, String date) {
        LambdaQueryWrapper<Reservation> wrapper = new LambdaQueryWrapper<>();
        if (status != null) {
            wrapper.eq(Reservation::getStatus, status);
        }
        if (date != null && !date.isEmpty()) {
            wrapper.eq(Reservation::getVisitDate, date);
        }
        wrapper.orderByDesc(Reservation::getCreateTime);
        return page(page, wrapper);
    }

    @Override
    public boolean updateStatus(Long id, Integer status) {
        Reservation reservation = getById(id);
        if (reservation == null) {
            return false;
        }
        reservation.setStatus(status);
        return updateById(reservation);
    }

    @Override
    public List<Reservation> getUserReservations(Long userId) {
        LambdaQueryWrapper<Reservation> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Reservation::getUserId, userId);
        wrapper.orderByDesc(Reservation::getCreateTime);
        return list(wrapper);
    }

    @Override
    public int countByDate(String date) {
        QueryWrapper<Reservation> wrapper = new QueryWrapper<>();
        wrapper.eq("visit_date", date);
        wrapper.ne("status", 2); // 不统计已取消的
        int count = (int)count(wrapper);
        return count;
    }

    @Override
    public List<Map<String, Object>> getPopularDates() {
        // 实现热门日期统计
        return reservationMapper.getPopularDates();
    }
    
    @Override
    public Map<String, Integer> getTicketAvailability(String startDate, String endDate) {
        try {
            // 获取博物馆每日最大预约数
            int maxReservation = getMaxReservationCount();
            
            // 获取指定日期范围内的预约信息
            Map<String, Integer> result = new HashMap<>();
            
            // 将起始日期和结束日期转为LocalDate
            LocalDate start = LocalDate.parse(startDate);
            LocalDate end = LocalDate.parse(endDate);
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
            
            // 获取指定日期范围内的已用票数
            Map<String, Integer> usedTickets = getUsedTicketsInRange(startDate, endDate);
            
            // 计算每日可用票数
            for (LocalDate date = start; !date.isAfter(end); date = date.plusDays(1)) {
                String formattedDate = date.format(formatter);
                
                // 获取或创建票务记录
                TicketDaily ticketDaily = getOrCreateTicketDaily(formattedDate, maxReservation);
                int totalTickets = ticketDaily.getTotalTickets();
                int used = ticketDaily.getUsedTickets();
                
                // 计算可用票数
                int available = totalTickets - used;
                if (available < 0) available = 0;
                
                result.put(formattedDate, available);
            }
            
            return result;
        } catch (Exception e) {
            log.error("获取票务可用情况失败", e);
            // 返回空结果而不是抛出异常
            return new HashMap<>();
        }
    }
    
    @Override
    public Integer getAvailableTickets(String date) {
        // 获取博物馆每日最大预约数
        int maxReservation = getMaxReservationCount();
        
        // 获取或创建票务记录
        TicketDaily ticketDaily = getOrCreateTicketDaily(date, maxReservation);
        
        // 计算可用票数
        int available = ticketDaily.getTotalTickets() - ticketDaily.getUsedTickets();
        return available > 0 ? available : 0;
    }
    
    @Override
    public List<Map<String, Object>> getAvailableTimeSlots(String date) {
        // 获取系统设置中的时间段
        SystemSettings settings = systemSettingsService.getSettings();
        
        // 默认时间段
        List<Map<String, Object>> defaultSlots = new ArrayList<>();
        Map<String, Object> morning = new HashMap<>();
        morning.put("timeSlot", "上午 9:00-12:00");
        morning.put("capacity", 100);
        morning.put("bookedCount", 0);
        
        Map<String, Object> afternoon = new HashMap<>();
        afternoon.put("timeSlot", "下午 13:00-16:00");
        afternoon.put("capacity", 100);
        afternoon.put("bookedCount", 0);
        
        defaultSlots.add(morning);
        defaultSlots.add(afternoon);
        
        // 如果系统设置有自定义时间段，则使用自定义时间段
        if (settings != null && !StringUtils.isEmpty(settings.getAvailableTimeSlots())) {
            try {
                List<Map<String, Object>> customSlots = objectMapper.readValue(
                        settings.getAvailableTimeSlots(), 
                        new TypeReference<List<Map<String, Object>>>() {});
                
                if (customSlots != null && !customSlots.isEmpty()) {
                    // 更新每个时间段的已预约人数
                    for (Map<String, Object> slot : customSlots) {
                        String timeSlot = (String) slot.get("timeSlot");
                        int bookedCount = getBookedCountForTimeSlot(date, timeSlot);
                        slot.put("bookedCount", bookedCount);
                    }
                    return customSlots;
                }
            } catch (JsonProcessingException e) {
                // 解析失败，使用默认时间段
            }
        }
        
        // 更新默认时间段的已预约人数
        for (Map<String, Object> slot : defaultSlots) {
            String timeSlot = (String) slot.get("timeSlot");
            int bookedCount = getBookedCountForTimeSlot(date, timeSlot);
            slot.put("bookedCount", bookedCount);
        }
        
        return defaultSlots;
    }
    
    /**
     * 获取博物馆每日最大预约数
     */
    private int getMaxReservationCount() {
        SystemSettings settings = systemSettingsService.getSettings();
        return settings != null && settings.getMaxReservation() != null ? 
                settings.getMaxReservation() : 200;
    }
    
    @Override
    @Transactional
    public boolean saveAndReduceTickets(Reservation reservation) {
        try {
            // 校验票数
            Integer availableTickets = getAvailableTickets(reservation.getVisitDate());
            
            if (availableTickets < reservation.getVisitorCount()) {
                return false;
            }
            
            // 设置默认状态
            if (reservation.getStatus() == null) {
                reservation.setStatus(0); // 0-待审核
            }
            
            // 保存预约信息
            boolean saved = save(reservation);
            if (!saved) {
                return false;
            }
            
            // 减少可用票数
            return reduceTickets(reservation.getVisitDate(), reservation.getVisitorCount());
        } catch (Exception e) {
            log.error("保存预约并减少票数失败", e);
            return false;
        }
    }
    
    @Override
    @Transactional
    public boolean cancelAndRestoreTickets(Long id) {
        try {
            // 获取预约信息
            Reservation reservation = getById(id);
            if (reservation == null) {
                return false;
            }
            
            // 更新状态为已取消
            reservation.setStatus(2); // 2-已取消
            boolean updated = updateById(reservation);
            if (!updated) {
                return false;
            }
            
            // 恢复票数
            return restoreTickets(reservation.getVisitDate(), reservation.getVisitorCount());
        } catch (Exception e) {
            log.error("取消预约并恢复票数失败", e);
            return false;
        }
    }
    
    /**
     * 减少指定日期的票数
     */
    @Override
    public boolean reduceTickets(String date, int count) {
        int maxReservation = getMaxReservationCount();
        TicketDaily ticketDaily = getOrCreateTicketDaily(date, maxReservation);
        
        ticketDaily.setUsedTickets(ticketDaily.getUsedTickets() + count);
        return ticketDailyMapper.updateById(ticketDaily) > 0;
    }
    
    /**
     * 恢复指定日期的票数
     */
    @Override
    public boolean restoreTickets(String date, int count) {
        int maxReservation = getMaxReservationCount();
        TicketDaily ticketDaily = getOrCreateTicketDaily(date, maxReservation);
        
        // 确保不会变成负数
        int newUsed = Math.max(0, ticketDaily.getUsedTickets() - count);
        ticketDaily.setUsedTickets(newUsed);
        return ticketDailyMapper.updateById(ticketDaily) > 0;
    }
    
    /**
     * 获取日期范围内的已用票数
     */
    private Map<String, Integer> getUsedTicketsInRange(String startDate, String endDate) {
        LambdaQueryWrapper<TicketDaily> wrapper = new LambdaQueryWrapper<>();
        wrapper.ge(TicketDaily::getDate, startDate);
        wrapper.le(TicketDaily::getDate, endDate);
        
        List<TicketDaily> ticketList = ticketDailyMapper.selectList(wrapper);
        Map<String, Integer> result = new HashMap<>();
        
        for (TicketDaily ticket : ticketList) {
            result.put(ticket.getDate(), ticket.getUsedTickets());
        }
        
        return result;
    }
    
    /**
     * 获取或创建票务记录
     */
    private TicketDaily getOrCreateTicketDaily(String date, Integer totalTickets) {
        try {
            // 查询是否已存在记录
            LambdaQueryWrapper<TicketDaily> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(TicketDaily::getDate, date);
            
            // 改为获取列表，而不是期望单条记录
            List<TicketDaily> ticketList = ticketDailyMapper.selectList(wrapper);
            TicketDaily ticketDaily = null;
            
            if (ticketList.isEmpty()) {
                // 如果不存在，则创建新记录
                ticketDaily = new TicketDaily();
                ticketDaily.setDate(date);
                ticketDaily.setTotalTickets(totalTickets);
                ticketDaily.setUsedTickets(0);
                ticketDaily.setCreateTime(LocalDateTime.now());
                ticketDaily.setUpdateTime(LocalDateTime.now());
                
                ticketDailyMapper.insert(ticketDaily);
            } else {
                // 如果存在记录，使用第一条
                ticketDaily = ticketList.get(0);
                
                // 检查记录中的最大票数是否与当前系统设置一致，如果不一致则更新
                if (!totalTickets.equals(ticketDaily.getTotalTickets())) {
                    log.info("更新日期[{}]的最大票数：{} -> {}", date, ticketDaily.getTotalTickets(), totalTickets);
                    ticketDaily.setTotalTickets(totalTickets);
                    ticketDaily.setUpdateTime(LocalDateTime.now());
                    ticketDailyMapper.updateById(ticketDaily);
                }
            }
            
            return ticketDaily;
        } catch (Exception e) {
            log.error("获取或创建票务记录失败", e);
            // 创建一个临时对象而不是抛出异常
            TicketDaily fallbackTicket = new TicketDaily();
            fallbackTicket.setDate(date);
            fallbackTicket.setTotalTickets(totalTickets);
            fallbackTicket.setUsedTickets(0);
            return fallbackTicket;
        }
    }
    
    /**
     * 获取指定时间段的已预约人数
     */
    private int getBookedCountForTimeSlot(String date, String timeSlot) {
        LambdaQueryWrapper<Reservation> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Reservation::getVisitDate, date);
        wrapper.eq(Reservation::getVisitTimeSlot, timeSlot);
        wrapper.ne(Reservation::getStatus, 2); // 不统计已取消的
        
        // 查询总预约人数
        return jdbcTemplate.queryForObject(
                "SELECT COALESCE(SUM(visitor_count), 0) FROM reservation WHERE visit_date = ? AND visit_time_slot = ? AND status != 2",
                Integer.class, date, timeSlot);
    }

    @Override
    public List<Map<String, Object>> getReservationVisitors(Long reservationId) {
        // 查询与预约关联的访客
        List<Map<String, Object>> visitors = new ArrayList<>();
        
        try {
            // SQL查询预约关联的访客信息
            String sql = "SELECT v.id, v.name, v.id_card, v.phone " +
                         "FROM visitor v " +
                         "JOIN reservation_visitor rv ON v.id = rv.visitor_id " +
                         "WHERE rv.reservation_id = ?";
            
            visitors = jdbcTemplate.queryForList(sql, reservationId);
        } catch (Exception e) {
            log.error("获取预约访客列表出错", e);
        }
        
        return visitors;
    }

    @Override
    @Transactional
    public boolean saveWithVisitors(Reservation reservation, List<Long> visitorIds) {
        try {
            // 校验票数
            Integer availableTickets = getAvailableTickets(reservation.getVisitDate());
            
            if (availableTickets < reservation.getVisitorCount()) {
                return false;
            }
            
            // 设置默认状态
            if (reservation.getStatus() == null) {
                reservation.setStatus(0); // 0-待审核
            }
            
            // 使用 JDBC 直接插入预约记录，避开 exhibition_id 字段问题
            String sql = "INSERT INTO reservation (user_id, visitor_count, visit_date, visit_time_slot, status, create_time, update_time) " +
                          "VALUES (?, ?, ?, ?, ?, ?, ?)";
            
            KeyHolder keyHolder = new GeneratedKeyHolder();
            jdbcTemplate.update(connection -> {
                PreparedStatement ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
                ps.setLong(1, reservation.getUserId());
                ps.setInt(2, reservation.getVisitorCount());
                ps.setString(3, reservation.getVisitDate());
                ps.setString(4, reservation.getVisitTimeSlot());
                ps.setInt(5, reservation.getStatus());
                ps.setTimestamp(6, java.sql.Timestamp.valueOf(reservation.getCreateTime()));
                ps.setTimestamp(7, java.sql.Timestamp.valueOf(reservation.getUpdateTime()));
                return ps;
            }, keyHolder);
            
            // 获取自增主键ID
            Long reservationId = keyHolder.getKey().longValue();
            reservation.setId(reservationId);
            
            // 如果有访客，关联预约与访客
            if (visitorIds != null && !visitorIds.isEmpty()) {
                // 批量插入预约-访客关系
                String visitorSql = "INSERT INTO reservation_visitor (reservation_id, visitor_id, create_time) VALUES (?, ?, ?)";
                List<Object[]> batchArgs = new ArrayList<>();
                
                LocalDateTime now = LocalDateTime.now();
                for (Long visitorId : visitorIds) {
                    batchArgs.add(new Object[]{reservationId, visitorId, java.sql.Timestamp.valueOf(now)});
                }
                
                jdbcTemplate.batchUpdate(visitorSql, batchArgs);
            }
            
            // 减少可用票数
            return reduceTickets(reservation.getVisitDate(), reservation.getVisitorCount());
        } catch (Exception e) {
            log.error("保存预约并关联访客失败", e);
            return false;
        }
    }

    /**
     * 获取预约关联的观众详细信息
     * @param reservationId 预约ID
     * @return 观众详细信息列表
     */
    @Override
    public List<Map<String, Object>> getReservationVisitorsDetail(Long reservationId) {
        List<Map<String, Object>> result = new ArrayList<>();
        
        try {
            // 查询预约与观众的关联记录
            QueryWrapper<ReservationVisitor> visitorQueryWrapper = new QueryWrapper<>();
            visitorQueryWrapper.eq("reservation_id", reservationId);
            List<ReservationVisitor> relationList = reservationVisitorMapper.selectList(visitorQueryWrapper);
            
            if (relationList != null && !relationList.isEmpty()) {
                // 获取所有访客ID
                List<Long> visitorIds = relationList.stream()
                    .map(ReservationVisitor::getVisitorId)
                    .collect(Collectors.toList());
                
                // 批量查询访客信息
                if (!visitorIds.isEmpty()) {
                    List<Visitor> visitors = visitorMapper.selectBatchIds(visitorIds);
                    
                    // 将访客信息转换为前端需要的格式
                    for (Visitor visitor : visitors) {
                        Map<String, Object> visitorMap = new HashMap<>();
                        visitorMap.put("id", visitor.getId());
                        visitorMap.put("name", visitor.getName());
                        visitorMap.put("idCard", visitor.getIdCard());
                        visitorMap.put("phone", visitor.getPhone());
                        result.add(visitorMap);
                    }
                }
            }
        } catch (Exception e) {
            log.error("获取预约观众详情失败", e);
        }
        
        return result;
    }

    /**
     * 更新预约信息和关联的访客
     * @param reservation 预约信息
     * @param visitorIds 访客ID列表
     * @return 是否成功
     */
    @Override
    @Transactional
    public boolean updateWithVisitors(Reservation reservation, List<Long> visitorIds) {
        try {
            // 更新预约基本信息
            boolean updated = updateById(reservation);
            if (!updated) {
                return false;
            }
            
            // 更新预约与访客的关联关系
            // 1. 删除原有关联
            QueryWrapper<ReservationVisitor> deleteWrapper = new QueryWrapper<>();
            deleteWrapper.eq("reservation_id", reservation.getId());
            reservationVisitorMapper.delete(deleteWrapper);
            
            // 2. 添加新的关联
            if (visitorIds != null && !visitorIds.isEmpty()) {
                List<ReservationVisitor> reservationVisitors = new ArrayList<>();
                LocalDateTime now = LocalDateTime.now();
                
                for (Long visitorId : visitorIds) {
                    ReservationVisitor rv = new ReservationVisitor();
                    rv.setReservationId(reservation.getId());
                    rv.setVisitorId(visitorId);
                    rv.setCreateTime(now);
                    reservationVisitors.add(rv);
                }
                
                // 批量插入新的关联关系
                for (ReservationVisitor rv : reservationVisitors) {
                    reservationVisitorMapper.insert(rv);
                }
            }
            
            return true;
        } catch (Exception e) {
            log.error("更新预约和访客关联失败", e);
            return false;
        }
    }
} 